﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Media;
using System.Security.Policy;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SoundPlayer = Tetris.SoundPlayer;

namespace TetrisGame
{


    /*
        Можно упростить.
        Методы Rotate и InitializeFigure получились слишком большими.
        1) Метод Rotate можно вынести из логики и посплитить если добавить новый абстрактный классы для каждой фигуры.
        2) InitializeFigure можно вынести в отдельный класс FigureInitializer (простое решение). Почитай про паттерн AbstractFactory (красивое решение)
    */
    class GameLogic 
    {
        private readonly GameField gameField;
        private readonly Drawer drawer;
        private readonly Random rnd;
        private List<Block> basement = new List<Block>();
        private InputHandler ctrl = new InputHandler();
        //private int score;
        private bool gameOver = false;
        private SoundPlayer _player;

        Block[] figure;
        Block[] testFigure={ new Block(0,0 , ConsoleColor.White), new Block(0,0, ConsoleColor.White),
                        new Block(0, 0, ConsoleColor.White), new Block(0, 0, ConsoleColor.White) };
        Shape currentFigureShape;
        Orientation currentOrientation = Orientation.N;

        public GameLogic(GameField gameField, Drawer drawer, SoundPlayer player)
        {
            this.gameField = gameField;
            this.drawer = drawer;
            _player = player;
            rnd = new Random();
        }

        public Block[] InitializeFigure()
        {
            currentOrientation = Orientation.N;
            switch (rnd.Next(0, 8))
            {
                case (0):
                    {
                        //Block[] figure - нужно ли создавать новый объект? скрывает поле figure класса GameLogic
                        Block[] figure = { new Block(gameField.X+4, gameField.Y-2, ConsoleColor.White), new Block(gameField.X+4,gameField.Y-1, ConsoleColor.White),
                        new Block(gameField.X+5, gameField.Y-2, ConsoleColor.White), new Block(gameField.X+5, gameField.Y-1, ConsoleColor.White) };
                        currentFigureShape = Shape.O;
                        return figure;
                    }
                case (1):
                    {
                        //Block[] figure - нужно ли создавать новый объект? скрывает поле figure класса GameLogic
                        Block[] figure = { new Block(gameField.X+4, gameField.Y-1, ConsoleColor.White), new Block(gameField.X+5,gameField.Y-2, ConsoleColor.White),
                        new Block(gameField.X+4, gameField.Y-2, ConsoleColor.White), new Block(gameField.X+4, gameField.Y-3, ConsoleColor.White) };
                        currentFigureShape = Shape.T;
                        return figure;
                    }

                case (2):
                    {
                        //Block[] figure - нужно ли создавать новый объект? скрывает поле figure класса GameLogic
                        Block[] figure = { new Block(gameField.X+5, gameField.Y-4, ConsoleColor.White), new Block(gameField.X+5,gameField.Y-3, ConsoleColor.White),
                        new Block(gameField.X+5, gameField.Y-2, ConsoleColor.White), new Block(gameField.X+5, gameField.Y-1, ConsoleColor.White) };
                        currentFigureShape = Shape.I;
                        return figure;
                    }
                case (3):
                    {
                        //Block[] figure - нужно ли создавать новый объект? скрывает поле figure класса GameLogic
                        Block[] figure = { new Block(gameField.X+4, gameField.Y-1, ConsoleColor.White), new Block(gameField.X+5,gameField.Y-1, ConsoleColor.White),
                        new Block(gameField.X+5, gameField.Y-2, ConsoleColor.White), new Block(gameField.X+5, gameField.Y-3, ConsoleColor.White) };
                        currentFigureShape = Shape.J;
                        return figure;
                    }
                case (4):
                    {
                        //Block[] figure - нужно ли создавать новый объект? скрывает поле figure класса GameLogic
                        Block[] figure = { new Block(gameField.X+5, gameField.Y-1, ConsoleColor.White), new Block(gameField.X+4,gameField.Y-1, ConsoleColor.White),
                        new Block(gameField.X+4, gameField.Y-2, ConsoleColor.White), new Block(gameField.X+4, gameField.Y-3, ConsoleColor.White) };
                        currentFigureShape = Shape.L;
                        return figure;
                    }
                case (5):
                    {
                        //Block[] figure - нужно ли создавать новый объект? скрывает поле figure класса GameLogic
                        Block[] figure = { new Block(gameField.X+4, gameField.Y-2, ConsoleColor.White), new Block(gameField.X+5,gameField.Y-2, ConsoleColor.White),
                        new Block(gameField.X+5, gameField.Y-1, ConsoleColor.White), new Block(gameField.X+6, gameField.Y-1, ConsoleColor.White) };
                        currentFigureShape = Shape.Z;
                        return figure;
                    }

                default:
                    {
                        //Block[] figure - нужно ли создавать новый объект? скрывает поле figure класса GameLogic
                        Block[] figure = { new Block(gameField.X+4, gameField.Y-1, ConsoleColor.White), new Block(gameField.X+5,gameField.Y-1, ConsoleColor.White),
                        new Block(gameField.X+5, gameField.Y-2, ConsoleColor.White), new Block(gameField.X+6, gameField.Y-2, ConsoleColor.White) };
                        currentFigureShape = Shape.S;
                        return figure;
                    }

            }
        }

        public void StartGame()
        {
            ctrl.KeyLeft += Left;
            ctrl.KeyUp += Rotate;
            ctrl.KeyRight += Right;
            //ctrl.KeyDown += Down;
            ctrl.KeyP += Pause;
            _player.PlayMusic();
            do
            {
                figure = InitializeFigure();
                bool fallen = false;
                do
                {
                    ctrl.QueryKB();
                    drawer.DrawFrameAndSpaces(gameField, '#');
                    drawer.DrawObjectList(figure, gameField);

                    if (TryMoveFigure(figure, Direction.Down)) Down(figure);
                    else
                    {
                        fallen = true;
                        foreach (var block in figure)
                        {
                            basement.Add(block);
                        }
                        for (int i = 0; i < figure.Length; i++)
                        {
                            int counter = 0;
                            for (int j = 0; j < basement.Count; j++)
                            {
                                if (basement[j].fullRow(figure[i].Y))
                                    counter++;
                            }
                            //counter == 10 - антипаттерн Magic Number
                            if (counter == 10)
                            {
                                basement.RemoveAll(x => x.Y == figure[i].Y);
                                foreach (var block in basement)
                                {
                                    if (block.Y < figure[i].Y) block.MoveDown();
                                }
                            }
                        }
                        drawer.DrawFrameAndSpaces(gameField, '#');
                    }
                    drawer.DrawObjectList(basement, gameField);
                    Thread.Sleep(200);

                }
                while (!fallen);


            }
            while (!gameOver);
        }


        //Block[] figure - нужен ли этот параметр в методе класса, если фигура хранится в поле класса?
        private bool TryMoveFigure(Block[] figure, Direction dir)
        {
            int y = 0;
            int x = 0;
            bool returnBool = true;
            foreach (var block in figure)
            {
                if (dir == Direction.Down)
                {
                    y = block.Y + 1;
                    x = block.X;
                }
                else if (dir == Direction.Right)
                {
                    y = block.Y;
                    x = block.X + 1;
                }
                else if (dir == Direction.Left)
                {
                    y = block.Y;
                    x = block.X - 1;
                }
                if (!gameField.IsOnField(x, y)) returnBool = false;

                foreach (var item in basement)
                {
                    if ((item.X == x && item.Y == y)) returnBool = false;
                }
            }
            return returnBool;
        }

        //Block[] testFigure - нужен ли этот параметр в методе класса, если тестовая фигура хранится в поле класса?
        private bool TryRotateFigure(Block[] testFigure)
        {
            bool returnBool = true;
            foreach (var block in testFigure)
            {
                if (!gameField.IsOnField(block.X, block.Y)) returnBool = false;

                foreach (var item in basement)
                {
                    if ((item.X == block.X && item.Y == block.Y)) returnBool = false;
                }
            }
            return returnBool;
        }

        private void Left(object sender, EventArgs e)
        {
            if (TryMoveFigure(figure, Direction.Left))
                foreach (var block in figure)
                {
                    block.X--;
                }
        }

        private void Right(object sender, EventArgs e)
        {
            if (TryMoveFigure(figure, Direction.Right))
                foreach (var block in figure)
                {
                    block.X++;
                }
        }

        //Block[] figure - нужен ли этот параметр в методе класса, если фигура хранится в поле класса?
        private void Down(Block[] figure)
        {
            if (TryMoveFigure(figure, Direction.Down))
                foreach (var block in figure)
                {
                    block.Y++;
                }
        }

        public void Rotate(object sender, EventArgs e)
        {
            for(int i=0;i<=3;i++)
            {
                testFigure[i].X = figure[i].X;
                testFigure[i].Y = figure[i].Y;
            }
            switch (currentFigureShape)
            {
                case (Shape.I):
                    {
                        switch (currentOrientation)
                        {
                            case (Orientation.N):
                                {
                                    testFigure[0].Y = testFigure[0].Y + 2;
                                    testFigure[0].X = testFigure[0].X - 2;
                                    testFigure[1].Y = testFigure[1].Y + 1;
                                    testFigure[1].X = testFigure[1].X - 1;
                                    testFigure[3].Y = testFigure[3].Y - 1;
                                    testFigure[3].X = testFigure[3].X + 1;
                                }
                                break;
                            case (Orientation.W):
                                {
                                    testFigure[0].Y = testFigure[0].Y + 2;
                                    testFigure[0].X = testFigure[0].X + 2;
                                    testFigure[1].Y = testFigure[1].Y + 1;
                                    testFigure[1].X = testFigure[1].X + 1;
                                    testFigure[3].Y = testFigure[3].Y - 1;
                                    testFigure[3].X = testFigure[3].X - 1;
                                }
                                break;
                            case (Orientation.S):
                                {
                                    testFigure[0].Y = testFigure[0].Y - 2;
                                    testFigure[0].X = testFigure[0].X + 2;
                                    testFigure[1].Y = testFigure[1].Y - 1;
                                    testFigure[1].X = testFigure[1].X + 1;
                                    testFigure[3].Y = testFigure[3].Y + 1;
                                    testFigure[3].X = testFigure[3].X - 1;
                                }
                                break;
                            case (Orientation.E):
                                {
                                    testFigure[0].Y = testFigure[0].Y - 2;
                                    testFigure[0].X = testFigure[0].X - 2;
                                    testFigure[1].Y = testFigure[1].Y - 1;
                                    testFigure[1].X = testFigure[1].X - 1;
                                    testFigure[3].Y = testFigure[3].Y + 1;
                                    testFigure[3].X = testFigure[3].X + 1;
                                }
                                break;
                        }
                    }
                    break;

                case (Shape.S):
                    {
                        switch (currentOrientation)
                        {
                            case (Orientation.N):
                                {
                                    testFigure[0].X = testFigure[0].X + 2;
                                    testFigure[1].Y = testFigure[1].Y - 1;
                                    testFigure[1].X = testFigure[1].X + 1;
                                    testFigure[3].Y = testFigure[3].Y - 1;
                                    testFigure[3].X = testFigure[3].X - 1;
                                }
                                break;
                            case (Orientation.W):
                                {
                                    testFigure[0].Y = testFigure[0].Y - 2;
                                    testFigure[1].Y = testFigure[1].Y - 1;
                                    testFigure[1].X = testFigure[1].X - 1;
                                    testFigure[3].Y = testFigure[3].Y + 1;
                                    testFigure[3].X = testFigure[3].X - 1;
                                }
                                break;
                            case (Orientation.S):
                                {
                                    testFigure[0].X = testFigure[0].X - 2;
                                    testFigure[1].Y = testFigure[1].Y + 1;
                                    testFigure[1].X = testFigure[1].X - 1;
                                    testFigure[3].Y = testFigure[3].Y + 1;
                                    testFigure[3].X = testFigure[3].X + 1;
                                }
                                break;
                            case (Orientation.E):
                                {
                                    testFigure[0].Y = testFigure[0].Y + 2;
                                    testFigure[1].Y = testFigure[1].Y + 1;
                                    testFigure[1].X = testFigure[1].X + 1;
                                    testFigure[3].Y = testFigure[3].Y - 1;
                                    testFigure[3].X = testFigure[3].X + 1;
                                }
                                break;


                        }
                        break;
                    }
                case (Shape.T):
                    {
                        switch (currentOrientation)
                        {
                            case (Orientation.N):
                                {
                                    testFigure[0].X = testFigure[0].X + 1;
                                    testFigure[0].Y = testFigure[0].Y - 1;
                                    testFigure[1].Y = testFigure[1].Y - 1;
                                    testFigure[1].X = testFigure[1].X - 1;
                                    testFigure[3].Y = testFigure[3].Y + 1;
                                    testFigure[3].X = testFigure[3].X - 1;
                                }
                                break;
                            case (Orientation.W):
                                {
                                    testFigure[0].X = testFigure[0].X - 1;
                                    testFigure[0].Y = testFigure[0].Y - 1;
                                    testFigure[1].Y = testFigure[1].Y + 1;
                                    testFigure[1].X = testFigure[1].X - 1;
                                    testFigure[3].Y = testFigure[3].Y + 1;
                                    testFigure[3].X = testFigure[3].X + 1;
                                }
                                break;
                            case (Orientation.S):
                                {
                                    testFigure[0].X = testFigure[0].X - 1;
                                    testFigure[0].Y = testFigure[0].Y + 1;
                                    testFigure[1].Y = testFigure[1].Y + 1;
                                    testFigure[1].X = testFigure[1].X + 1;
                                    testFigure[3].Y = testFigure[3].Y - 1;
                                    testFigure[3].X = testFigure[3].X + 1;
                                }
                                break;
                            case (Orientation.E):
                                {
                                    testFigure[0].X = testFigure[0].X + 1;
                                    testFigure[0].Y = testFigure[0].Y + 1;
                                    testFigure[1].Y = testFigure[1].Y - 1;
                                    testFigure[1].X = testFigure[1].X + 1;
                                    testFigure[3].Y = testFigure[3].Y - 1;
                                    testFigure[3].X = testFigure[3].X - 1;
                                }
                                break;


                        }
                        break;
                    }
                case (Shape.J):
                    {
                        switch (currentOrientation)
                        {
                            case (Orientation.N):
                                {
                                    testFigure[0].X = testFigure[0].X + 1;
                                    testFigure[0].Y = testFigure[0].Y - 1;
                                    testFigure[1].Y = testFigure[1].Y - 2;
                                    testFigure[2].Y = testFigure[2].Y - 1;
                                    testFigure[2].X = testFigure[2].X - 1;
                                    testFigure[3].X = testFigure[3].X - 2;
                                }
                                break;
                            case (Orientation.W):
                                {
                                    testFigure[0].X = testFigure[0].X - 1;
                                    testFigure[0].Y = testFigure[0].Y - 1;
                                    testFigure[1].X = testFigure[1].X - 2;
                                    testFigure[2].Y = testFigure[2].Y + 1;
                                    testFigure[2].X = testFigure[2].X - 1;
                                    testFigure[3].Y = testFigure[3].Y + 2;
                                }
                                break;
                            case (Orientation.S):
                                {
                                    testFigure[0].X = testFigure[0].X - 1;
                                    testFigure[0].Y = testFigure[0].Y + 1;
                                    testFigure[1].Y = testFigure[1].Y + 2;
                                    testFigure[2].Y = testFigure[2].Y + 1;
                                    testFigure[2].X = testFigure[2].X + 1;
                                    testFigure[3].X = testFigure[3].X + 2;
                                }
                                break;
                            case (Orientation.E):
                                {
                                    testFigure[0].X = testFigure[0].X + 1;
                                    testFigure[0].Y = testFigure[0].Y + 1;
                                    testFigure[1].X = testFigure[1].X + 2;
                                    testFigure[2].Y = testFigure[2].Y - 1;
                                    testFigure[2].X = testFigure[2].X + 1;
                                    testFigure[3].Y = testFigure[3].Y - 2;
                                }
                                break;


                        }
                        break;
                    }
                case (Shape.L):
                    {
                        switch (currentOrientation)
                        {
                            case (Orientation.N):
                                {
                                    testFigure[0].X = testFigure[0].X + 1;
                                    testFigure[0].Y = testFigure[0].Y - 1;
                                    testFigure[1].X = testFigure[1].X + 2;
                                    testFigure[2].Y = testFigure[2].Y + 1;
                                    testFigure[2].X = testFigure[2].X + 1;
                                    testFigure[3].Y = testFigure[3].Y + 2;
                                }
                                break;
                            case (Orientation.W):
                                {
                                    testFigure[0].X = testFigure[0].X - 1;
                                    testFigure[0].Y = testFigure[0].Y - 1;
                                    testFigure[1].Y = testFigure[1].Y - 2;
                                    testFigure[2].Y = testFigure[2].Y - 1;
                                    testFigure[2].X = testFigure[2].X + 1;
                                    testFigure[3].X = testFigure[3].X + 2;
                                }
                                break;
                            case (Orientation.S):
                                {
                                    testFigure[0].X = testFigure[0].X - 1;
                                    testFigure[0].Y = testFigure[0].Y + 1;
                                    testFigure[1].X = testFigure[1].X - 2;
                                    testFigure[2].Y = testFigure[2].Y - 1;
                                    testFigure[2].X = testFigure[2].X - 1;
                                    testFigure[3].Y = testFigure[3].Y - 2;
                                }
                                break;
                            case (Orientation.E):
                                {
                                    testFigure[0].X = testFigure[0].X + 1;
                                    testFigure[0].Y = testFigure[0].Y + 1;
                                    testFigure[1].Y = testFigure[1].Y + 2;
                                    testFigure[2].Y = testFigure[2].Y + 1;
                                    testFigure[2].X = testFigure[2].X - 1;
                                    testFigure[3].X = testFigure[3].X - 2;
                                }
                                break;
                        }
                    }
                    break;
                case (Shape.Z):
                    {
                        switch (currentOrientation)
                        {
                            case (Orientation.N):
                                {
                                    testFigure[0].Y = testFigure[0].Y + 2;
                                    testFigure[1].X = testFigure[1].X - 1;
                                    testFigure[1].Y = testFigure[1].Y + 1;
                                    testFigure[3].X = testFigure[3].X - 1;
                                    testFigure[3].Y = testFigure[3].Y - 1;
                                }
                                break;
                            case (Orientation.W):
                                {
                                    testFigure[0].X = testFigure[0].X + 2;
                                    testFigure[1].X = testFigure[1].X + 1;
                                    testFigure[1].Y = testFigure[1].Y + 1;
                                    testFigure[3].X = testFigure[3].X - 1;
                                    testFigure[3].Y = testFigure[3].Y + 1;
                                }
                                break;
                            case (Orientation.S):
                                {
                                    testFigure[0].Y = testFigure[0].Y - 2;
                                    testFigure[1].Y = testFigure[1].Y - 1;
                                    testFigure[1].X = testFigure[1].X + 1;
                                    testFigure[3].X = testFigure[3].X + 1;
                                    testFigure[3].Y = testFigure[3].Y + 1;
                                }
                                break;
                            case (Orientation.E):
                                {
                                    testFigure[0].X = testFigure[0].X - 2;
                                    testFigure[1].X = testFigure[1].X - 1;
                                    testFigure[1].Y = testFigure[1].Y - 1;
                                    testFigure[3].X = testFigure[3].X + 1;
                                    testFigure[3].Y = testFigure[3].Y - 1;
                                }
                                break;
                        }
                    }
                    break;
            }
            if (TryRotateFigure(testFigure))
            {
                for (int i = 0; i <= 3; i++)
                {
                    figure[i].X = testFigure[i].X;
                    figure[i].Y = testFigure[i].Y;
                }
                if (currentOrientation == Orientation.N) currentOrientation = Orientation.W;
                else if (currentOrientation == Orientation.W) currentOrientation = Orientation.S;
                else if (currentOrientation == Orientation.S) currentOrientation = Orientation.E;
                else if (currentOrientation == Orientation.E) currentOrientation = Orientation.N;
            }

        }



        //public void Down(object sender, EventArgs e)
        //{
        //    if (currentDirection != Direction.Up)
        //        currentDirection = Direction.Down;


        public void Pause(object sender, EventArgs e)
        {
            ConsoleKeyInfo key;
            do
            {
                key = Console.ReadKey(true);
            }
            while (key.Key != ConsoleKey.P);
        }
    }
}


enum Direction
{
    Down,
    Left,
    Right
}
enum Shape
{
    O,
    L,
    I,
    J,
    S,
    Z,
    T
}
enum Orientation
{
    N,
    W,
    S,
    E
}

